package com.pick.admin.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.pick.admin.dto.LoginResponse;
import com.pick.admin.entity.SysMenu;
import com.pick.admin.entity.SysUser;
import com.pick.admin.mapper.SysMenuMapper;
import com.pick.admin.service.AuthService;
import com.pick.admin.service.SysUserService;
import com.pick.base.constants.SystemConstants;
import com.pick.base.exception.BusinessException;
import com.pick.base.exception.UserNotFoundException;
import com.pick.base.util.PasswordUtil;
import com.pick.base.util.ValidationUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 认证服务实现类
 */
@Service
@Slf4j
public class AuthServiceImpl implements AuthService {

    @Autowired
    private SysUserService userService;

    @Autowired
    private SysMenuMapper menuMapper;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public LoginResponse login(String username, String password) {
        // 检查登录失败次数
        checkLoginFailCount(username);
        
        // 查询用户
        SysUser user = userService.getByUsername(username);
        if (user == null) {
            // 记录登录失败
            recordLoginFail(username);
            throw new UserNotFoundException("用户名或密码错误");
        }

        // 验证密码
        if (!PasswordUtil.verify(password, user.getPassword())) {
            // 记录登录失败
            recordLoginFail(username);
            throw new RuntimeException("用户名或密码错误");
        }

        // 检查用户状态
        if (!SystemConstants.UserStatus.ENABLED.equals(user.getStatus())) {
            throw new RuntimeException("用户已被禁用");
        }

        // 登录成功，清除失败记录
        clearLoginFailCount(username);
        
        // 登录
        StpUtil.login(user.getId());
        
        log.info("用户登录成功: {}", username);

        // 更新最后登录时间
        user.setLastLoginTime(LocalDateTime.now());
        userService.updateById(user);

        // 构建登录响应
        String token = StpUtil.getTokenValue();
        Long expiresIn = StpUtil.getTokenTimeout();
        
        return new LoginResponse(token, expiresIn, user.getId(), user.getUsername(), user.getRealName());
    }

    @Override
    public void logout() {
        StpUtil.logout();
    }

    @Override
    public SysUser getCurrentUser() {
        Long userId = StpUtil.getLoginIdAsLong();
        return userService.getById(userId);
    }

    @Override
    public List<SysMenu> getCurrentUserMenus() {
        Long userId = StpUtil.getLoginIdAsLong();
        return menuMapper.selectMenusByUserId(userId);
    }

    @Override
    @Cacheable(value = "user:permissions", key = "#root.target.getCurrentUserId()")
    public List<String> getCurrentUserPermissions() {
        Long userId = StpUtil.getLoginIdAsLong();
        return userService.getUserPermissionCodes(userId);
    }

    @Override
    @Cacheable(value = "user:roles", key = "#root.target.getCurrentUserId()")
    public List<String> getCurrentUserRoles() {
        Long userId = StpUtil.getLoginIdAsLong();
        return userService.getUserRoleCodes(userId);
    }
    
    /**
     * 获取当前用户ID
     */
    public Long getCurrentUserId() {
        return StpUtil.getLoginIdAsLong();
    }
    
    /**
     * 检查登录失败次数
     */
    private void checkLoginFailCount(String username) {
        String key = SystemConstants.LoginFail.CACHE_KEY_PREFIX + username;
        Integer failCount = (Integer) redisTemplate.opsForValue().get(key);
        if (failCount != null && failCount >= SystemConstants.LoginFail.MAX_FAIL_COUNT) {
            throw new RuntimeException("账户已被锁定，请" + (SystemConstants.LoginFail.LOCK_TIME / 60) + "分钟后重试");
        }
    }
    
    /**
     * 记录登录失败
     */
    private void recordLoginFail(String username) {
        String key = SystemConstants.LoginFail.CACHE_KEY_PREFIX + username;
        Integer failCount = (Integer) redisTemplate.opsForValue().get(key);
        failCount = failCount == null ? 1 : failCount + 1;
        redisTemplate.opsForValue().set(key, failCount, SystemConstants.LoginFail.LOCK_TIME, TimeUnit.SECONDS);
        
        log.warn("用户登录失败: {}, 失败次数: {}", username, failCount);
    }
    
    /**
     * 清除登录失败记录
     */
    private void clearLoginFailCount(String username) {
        String key = SystemConstants.LoginFail.CACHE_KEY_PREFIX + username;
        redisTemplate.delete(key);
    }

    @Override
    public SysUser register(String username, String password, String realName, String email, String mobile) {
        // 参数验证
        validateRegisterParams(username, password, realName, email, mobile);
        
        // 检查用户名是否已存在
        if (userService.getByUsername(username) != null) {
            throw new BusinessException("用户名已存在");
        }
        
        // 检查邮箱是否已存在
        if (userService.getByEmail(email) != null) {
            throw new BusinessException("邮箱已被注册");
        }
        
        // 检查手机号是否已存在
        if (userService.getByMobile(mobile) != null) {
            throw new BusinessException("手机号已被注册");
        }
        
        // 创建用户对象
        SysUser user = new SysUser();
        user.setUsername(username);
        user.setPassword(PasswordUtil.encrypt(password));
        user.setRealName(realName);
        user.setEmail(email);
        user.setMobile(mobile);
        user.setStatus(SystemConstants.UserStatus.ENABLED);
        
        // 保存用户
        boolean success = userService.createUser(user);
        if (!success) {
            throw new BusinessException("注册失败，请稍后重试");
        }
        
        log.info("用户注册成功: {}", username);
        return user;
    }
    
    /**
     * 验证注册参数
     */
    private void validateRegisterParams(String username, String password, String realName, String email, String mobile) {
        // 验证用户名
        if (!ValidationUtil.isValidUsername(username)) {
            throw new BusinessException("用户名格式不正确，只能包含字母、数字和下划线，长度3-20位");
        }
        
        // 验证密码强度
        if (!ValidationUtil.isValidPassword(password)) {
            throw new BusinessException("密码强度不够，至少8位，包含大小写字母、数字和特殊字符");
        }
        
        // 验证真实姓名
        if (realName == null || realName.trim().isEmpty() || realName.length() > 50) {
            throw new BusinessException("真实姓名不能为空且长度不能超过50个字符");
        }
        
        // 验证邮箱
        if (!ValidationUtil.isValidEmail(email)) {
            throw new BusinessException("邮箱格式不正确");
        }
        
        // 验证手机号
        if (!ValidationUtil.isValidPhone(mobile)) {
            throw new BusinessException("手机号格式不正确");
        }
        
        // 安全检查
        if (ValidationUtil.containsSqlInjection(username) || 
            ValidationUtil.containsSqlInjection(realName) ||
            ValidationUtil.containsXss(username) ||
            ValidationUtil.containsXss(realName)) {
            throw new BusinessException("输入内容包含非法字符");
        }
    }
}